/**
 * check if a string is a valid date <br>
 * 
 * @author Yoshimi
 * @param value
 *            the string to be check (required) null or empty string will pass
 *            the check
 * @param options
 *            the various check options (optional)<br> + <b>strict</b>: if set
 *            to true, the values of date, month, year must be valid for the
 *            check to pass. Otherwise, the check passes as long as a valid date
 *            can be generated from the string. Used with caution.<br>
 *            The default value is true.<br> + <b>dayIndex</b>, <b>monthIndex</b>,
 *            <b>yearIndex</b>: specify the position of the day, month, year in
 *            the string. For example in yyyy/mm/dd, yearIndex is 0, monthIndex
 *            is 1, and dayIndex is 2.<br>
 *            The default values are dayIndex = 0, monthIndex = 1, yearIndex = 2<br> +
 *            <b>regexp</b>: the regular expression for the delimiter.<br>
 *            The default value is /[\/\+\:.t-]/gi
 */
function isDate(value, options) {
	// null, blank is valid
	if (!value) {
		return true;
	}

	if (options == null) {
		options = new Object();
	}

	// default strict mode = true
	if (options.strict == null) {
		options.strict = true;
	}
	// specify the position of day value in the text, ie dd/mm/yyyy -> 0
	// specify the position of month value in the text, ie dd/mm/yyyy -> 1
	// specify the position of year value in the text, ie dd/mm/yyyy -> 2
	if ((options.dayIndex == null || (isNaN(options.dayIndex) && options.dayIndex != 0))
			&& (options.monthIndex == null || (isNaN(options.monthIndex) && options.monthIndex != 0))
			&& (options.yearIndex == null || (isNaN(options.yearIndex) && options.yearIndex != 0))) {
		options.dayIndex = 0;
		options.monthIndex = 1;
		options.yearIndex = 2;
	}
	// specify the regular expression for the delimiter
	if (options.regexp == null) {
		options.regexp = /[\/\+\:.t-]/gi;
	}
	var strs = value.split(options.regexp);
	var date;
	var pass = false;
	try {
		// alert(options.yearIndex + "/" + options.monthIndex + "/" +
		// options.dayIndex + "\n" /*+
		// strs[options.yearIndex] + "/" + strs[options.monthIndex] - 1 + "/" +
		// strs[options.dayIndex]*/);
		date = new Date(strs[options.yearIndex], strs[options.monthIndex] - 1,
				strs[options.dayIndex]);
		if (options.strict) {
			if (date.getDate() == strs[options.dayIndex]
					&& date.getMonth() + 1 == strs[options.monthIndex]
					&& (date.getYear() == strs[options.yearIndex] || date
							.getFullYear() == strs[options.yearIndex])) {
				pass = true;
			}
		} else {
			pass = !isNaN(date);
			alert(date);
		}
	} catch (e) {
	}
	return pass;
}

/**
 * convert a string to a date <br>
 * 
 * @author Yoshimi
 * @param value
 *            the string to be check (required) null or empty string will pass
 *            the check
 * @param options
 *            the various check options (optional)<br> + <b>dayIndex</b>,
 *            <b>monthIndex</b>, <b>yearIndex</b>: specify the position of the
 *            day, month, year in the string. For example in yyyy/mm/dd,
 *            yearIndex is 0, monthIndex is 1, and dayIndex is 2.<br>
 *            The default values are dayIndex = 0, monthIndex = 1, yearIndex = 2<br> +
 *            <b>regexp</b>: the regular expression for the delimiter.<br>
 *            The default value is /[\/\+\:.t-]/gi
 */
function toDate(string, options) {
	// null, blank is valid
	if (!string) {
		return null;
	}

	if (options == null) {
		options = new Object();
	}
	// specify the position of day value in the text, ie dd/mm/yyyy -> 0
	// specify the position of month value in the text, ie dd/mm/yyyy -> 1
	// specify the position of year value in the text, ie dd/mm/yyyy -> 2
	if ((options.dayIndex == null || (isNaN(options.dayIndex) && options.dayIndex != 0))
			&& (options.monthIndex == null || (isNaN(options.monthIndex) && options.monthIndex != 0))
			&& (options.yearIndex == null || (isNaN(options.yearIndex) && options.yearIndex != 0))) {
		options.dayIndex = 0;
		options.monthIndex = 1;
		options.yearIndex = 2;
	}
	// specify the regular expression for the delimiter
	if (options.regexp == null) {
		options.regexp = /[\/\+\:.t-]/gi;
	}
	var strs = string.split(options.regexp);
	var date;
	try {
		// alert(options.yearIndex + "/" + options.monthIndex + "/" +
		// options.dayIndex + "\n" /*+
		// strs[options.yearIndex] + "/" + strs[options.monthIndex] - 1 + "/" +
		// strs[options.dayIndex]*/);
		date = new Date(strs[options.yearIndex], strs[options.monthIndex] - 1,
				strs[options.dayIndex]);
		if (!isNaN(date)) {
			return date;
		}
	} catch (e) {
	}
	return null;
}

/**
 * @param yearId
 * @param monthId
 * @param dayId
 * @param options
 *            <br> - <i>startYear</i><br> - <i>endYear</i><br> -
 *            <i>selectedYear</i><br> - <i>selectedMonth</i><br> -
 *            <i>selectedDay</i><br> - <i>firstText</i><br> - <i>firstValue</i>
 */
function initDateSelect(yearId, monthId, dayId, options) {
	var yearNode;
	var monthNode;
	var dayNode;
	var startYear;
	var endYear;
	var selectedYear;
	var selectedMonth;
	var selectedDay;
	var firstText;
	var firstValue;
	var opts;
	// default startYear
	if (!options || options.startYear == null) {
		startYear = new Date().getYear() - 5;
	} else {
		startYear = options.startYear;
	}
	// default endYear
	if (!options || options.endYear == null) {
		endYear = startYear + 5;
	} else {
		endYear = options.endYear;
	}
	// default selectedYear
	if (!options || options.selectedYear == null) {
		selectedYear = endYear;
	} else {
		selectedYear = options.selectedYear;
	}
	// default selectedMonth
	if (!options || options.selectedMonth == null) {
		selectedMonth = 1;
	} else {
		selectedMonth = options.selectedMonth;
	}
	// default selectedDay
	if (!options || options.selectedDay == null) {
		selectedDay = 1;
	} else {
		selectedDay = options.selectedDay;
	}
	// default firstText
	if (!options || options.firstText == null) {
		firstText = "All";
	} else {
		firstText = options.firstText;
	}
	// default firstValue
	if (!options || options.firstValue == null) {
		firstValue = "";
	} else {
		firstValue = options.firstValue;
	}
	yearNode = document.getElementById(yearId);
	monthNode = document.getElementById(monthId);
	dayNode = document.getElementById(dayId);
	// fill year select
	opts = new Object();
	opts.selected = selectedYear;
	opts.firstText = firstText;
	opts.firstValue = firstValue;
	fillNumberSelect(yearNode, startYear, endYear, opts);
	// fill month
	opts = new Object();
	opts.selected = selectedMonth;
	opts.firstText = firstText;
	opts.firstValue = firstValue;
	fillNumberSelect(monthNode, 1, 12, opts);
	// fill day
	opts = new Object();
	opts.selected = selectedDay;
	opts.firstText = firstText;
	opts.firstValue = firstValue;
	fillNumberSelect(dayNode, 1, daysInMonth(selectedMonth, selectedYear), opts);
	// update day select when month and year select change
	opts = new Object();
	opts.firstText = firstText;
	opts.firstValue = firstValue;
	monthNode.addEventListener('change', function() {
		setDaySelect(dayNode, monthNode, yearNode, opts);
	}, false);
	opts = new Object();
	opts.firstText = firstText;
	opts.firstValue = firstValue;
	yearNode.addEventListener('change', function() {
		setDaySelect(dayNode, monthNode, yearNode, opts);
	}, false);
}

// Update a day select element in accordance to the month and year elements
function setDaySelect(dayNote, monthNode, yearNode, options) {
	var year = yearNode.options[yearNode.selectedIndex].value;
	var month = monthNode.options[monthNode.selectedIndex].value;
	var day = dayNote.options[dayNote.selectedIndex].value;
	var firstText = "";
	var firstValue = "";
	var opts;
	if (options && options.firstText) {
		firstText = options.firstText;
		if (options.firstValue) {
			firstValue = options.firstValue;
		}
	}
	// refill day select when month and year are selected
	if (month != firstValue && year != firstValue) {
		opts = new Object();
		opts.selected = day;
		opts.firstText = firstText;
		opts.firstValue = firstValue;
		fillNumberSelect(dayNote, 1, daysInMonth(month, year), opts);
	}
}

// Get number of days in a specific month
function daysInMonth(month, year) {
	return new Date(year, month, 0).getDate();
}

// fill a select element with numeric options
function fillNumberSelect(selectNode, start, end, options) {
	if (selectNode && start && end) {
		// clear select
		selectNode.options.length = 0;
		if (!options) {
			options = new Object();
		}
		// add default blank option
		if (options.firstText) {
			if (options.firstValue) {
				selectNode.options[selectNode.options.length] = new Option(options.firstText, options.firstValue);
			} else {
				selectNode.options[selectNode.options.length] = new Option(options.firstText, "");
			}
		}
		for ( var i = start; i <= end; i++) {
			if (options.selected == i) {
				selectNode.options[selectNode.options.length] = new Option(i, i,
						true, true);
			} else {
				selectNode.options[selectNode.options.length] = new Option(i, i);
			}
		}
	}
}